[Interactive Graph] Add logarithm math utilities to kmath#3421
[Interactive Graph] Add logarithm math utilities to kmath#3421
Conversation
🗄️ Schema Change: No Changes ✅ |
🛠️ Item Splitting: No Changes ✅ |
|
Size Change: +143 B (+0.03%) Total Size: 495 kB
ℹ️ View Unchanged
|
npm Snapshot: PublishedGood news!! We've packaged up the latest commit from this PR (473ae00) and published it to npm. You Example: pnpm add @khanacademy/perseus@PR3421If you are working in Khan Academy's frontend, you can run the below command. ./dev/tools/bump_perseus_version.ts -t PR3421If you are working in Khan Academy's webapp, you can run the below command. ./dev/tools/bump_perseus_version.js -t PR3421 |
|
@claude review |
There was a problem hiding this comment.
LGTM — clean, well-tested math utility following the existing exponential pattern.
Extended reasoning...
Overview
Adds LogarithmCoefficient type and getLogarithmCoefficients() to packages/kmath/src/coefficients.ts, exports the type in index.ts, and adds a changeset file. 4 files changed, all additive.
Mathematical correctness
The inverse-exponential derivation is sound. For f(x) = a·ln(b·x + c), flipping coordinates transforms the problem into fitting an exponential x = aExp·e^(bExp·y) + cExp where cExp = asymptote. Inverting gives a = 1/bExp, b = 1/aExp, c = -cExp/aExp — all verified algebraically. The validation guards (same-y, point on asymptote, opposite sides, non-finite/zero intermediates) correctly cover degenerate cases, including the extra bExp === 0 guard needed because a = 1/bExp.
Security risks
None. This is a pure math utility with no I/O or user-input handling.
Level of scrutiny
Low. The change is additive-only, follows an established pattern (getExponentialCoefficients), and all checks passed (tsc, lint, prettier, knip). The 6 new tests exercise both happy paths and all four error/invalid-input cases.
Other factors
No CODEOWNERS concerns for this kmath utility file. Bundle impact is +134 B, as expected.
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
SonicScrewdriver
left a comment
There was a problem hiding this comment.
Looks great to me! The graph appears to work well when I manually test as well
…ema (#3420) ## Summary: PR series to add logarithm graph support to the Interactive Graph widget: 1.▶️ [Add logarithm graph type definitions and data](#3420) 2. [Add logarithm math utilities to kmath](#3421) 3. [Add logarithm graph state management and reducer](#3422) 4. [Add logarithm graph rendering, SR strings, and equation string](#3423) 5. [Add logarithm graph scoring](#3424) 6. [Add logarithm graph option in the Interactive Graph Editor](#3425) Add logarithm type definitions, this is the initial implementation for supporting Logarithm graph in Interactive Graph widget. - Add `PerseusGraphTypeLogarithm` and `LogarithmGraphCorrect` types to the data schema, following the exponential pattern (coords + asymptote) - Add JSON parser for the new `"logarithm"` graph type - Add `generateIGLogarithmGraph()` test data generator - Add placeholder cases in all exhaustiveness switches so the build stays green ## Details This is the first PR in the logarithm interactive graph series (LEMS-3950). It adds the type definitions with zero runtime behavior change — no graph renders, no scoring, no editor UI. **Data shape:** Logarithm follows the exponential pattern — two curve points plus an asymptote value. For logarithm the asymptote is the x-value of a vertical line (vs. exponential's y-value for a horizontal line). **Placeholder cases** were added to satisfy `UnreachableCaseError` exhaustiveness in: - `interactive-graph-editor.tsx` — graph merging switch - `start-coords/util.ts` — `shouldShowStartCoordsUI` (returns `false`), `getDefaultGraphStartCoords` (returns `undefined`) - `interactive-graph.tsx` — `getEquationString` (returns `""` — safe no-op since this is called unconditionally in the editor render path) - `initialize-graph-state.ts` — `initializeGraphState` (returns `type: "none"`) - `interactive-graph-ai-utils.ts` — `getGraphOptionsForProps` and `getUserInput` These placeholders will be replaced with real implementations in subsequent PRs. **Why `getEquationString` returns `""` instead of throwing:** `getEquationString` is called unconditionally in the editor's `render()` method (`interactive-graph-editor.tsx`). Since this PR adds the parser that accepts `type: "logarithm"`, any content with `correct.type === "logarithm"` (e.g. created via API) would reach the editor and crash the React render if the placeholder threw. All other placeholders in this PR are safe (return no-op values), but `getEquationString` is uniquely on the render path, so it must return a safe value. Future graph types should follow the same pattern: return `""` here, not throw. Issue: LEMS-3953 Co-Authored by Claude Code (Opus) ## Test plan - [ ] `pnpm tsc` passes - [ ] `pnpm knip` passes - [ ] `pnpm lint` passes - [ ] `pnpm prettier . --check` passes - [ ] Generator tests pass (2 new tests: default graph, graph with all props) - [ ] Parser regression tests pass (170 snapshots unchanged) - [ ] Interactive graph tests pass (199 tests) - [ ] Verify no runtime behavior change in Storybook (existing graph types still work) Author: ivyolamit Reviewers: claude[bot], ivyolamit, SonicScrewdriver, handeyeco Required Reviewers: Approved By: SonicScrewdriver Checks: ⏭️ 1 check has been skipped, ✅ 10 checks were successful Pull Request URL: #3420
…th utilities to kmath for supporting Logarithm graph in Interactive Graph
…h utilities to kmath
48b121b to
4f8ea19
Compare
…tion to note the post-inversion check for getLogarithmCoefficients
There was a problem hiding this comment.
LGTM — the isFinite guard on final coefficients is present, the algorithm follows the established exponential pattern correctly, and the 6 new tests cover the key cases.
Extended reasoning...
Overview
This PR adds getLogarithmCoefficients() and LogarithmCoefficient type to kmath, following the established getExponentialCoefficients pattern. It modifies 4 files: a changeset, the coefficients source, its test file, and the package index. The function is a pure math utility computing {a,b,c} for f(x) = a·ln(b·x+c) via an inverse-exponential approach.
Security risks
None. This is a pure math utility with no I/O, no auth, no data storage, and no user-controlled code paths beyond the coordinate inputs.
Level of scrutiny
Medium — this is a new public API function in a shared library that downstream PRs 4 and 5 will consume. However, the implementation is self-contained, matches the reference in grapher-util.ts, and follows the exact same algorithmic pattern as the sibling getExponentialCoefficients.
Assessment of prior concern
My earlier inline comment flagged a missing isFinite check on the final inverted coefficients. The current code already includes if (\!isFinite(a) || \!isFinite(b) || \!isFinite(c)) { return; } after the inversion step — this was addressed (or was always present). The guards are now consistent with the sibling function.
Other factors
The two remaining bug reports are (1) a pre-existing issue in getExponentialCoefficients that this PR does not modify, and (2) a nit about a missing same-x test case and JSDoc omission — both are minor and do not affect correctness. The 6 new tests cover the mathematically meaningful invalid-input cases.
| getLogarithmCoefficients( | ||
| [ | ||
| [0, 1], // x === asymptote x | ||
| [2, 3], | ||
| ], | ||
| 0, | ||
| ), | ||
| ).toBeUndefined(); | ||
| }); | ||
|
|
||
| it("returns undefined when points are on opposite sides of the asymptote", () => { | ||
| expect( | ||
| getLogarithmCoefficients( | ||
| [ | ||
| [-1, 1], | ||
| [1, 2], | ||
| ], | ||
| 0, | ||
| ), | ||
| ).toBeUndefined(); | ||
| }); | ||
| }); | ||
|
|
||
| describe("getTangentCoefficients", () => { | ||
| it("returns correct coefficients for basic tangent", () => { | ||
| // Simplest case: inflection at origin, p2 one quarter-period away. |
There was a problem hiding this comment.
🟡 The JSDoc for getLogarithmCoefficients omits the same-x-coordinate case from its list of invalid inputs, and there is no test covering this code path, unlike the sibling getExponentialCoefficients which documents and tests its analogous same-x case. The function does correctly return undefined when p1[0]===p2[0] (via the bExp===0 guard), but this behavior is undocumented and untested.
Extended reasoning...
What the bug is:
The getLogarithmCoefficients JSDoc (coefficients.ts ~line 100) describes three invalid-input cases: "same y, a point on the asymptote, or points on opposite sides of the asymptote." The same-x-coordinate case is conspicuously absent, and there is no corresponding test in coefficients.test.ts (lines 158–183), unlike getExponentialCoefficients which has an explicit test titled "returns undefined when both points share the same x-coordinate" (line ~56).
Why the refutation misses the point:
The refutation correctly notes the function is functionally correct — this is not in dispute. The issue is a documentation and test coverage gap. Good software practices require that: (1) documented behavior matches actual behavior, (2) all meaningful code paths are tested, and (3) sibling functions follow consistent patterns. The refutation argues "missing tests or incomplete JSDoc are code quality suggestions, not bugs" — but by the same logic, nothing is ever a bug if the computation returns an answer. The getExponentialCoefficients function sets the expectation for how these functions should be documented and tested.
Step-by-step proof of the undocumented code path:
Given p1=[3,1], p2=[3,5], asymptote=0:
p1[1] \!== p2[1](1 ≠ 5) — same-y guard passesp1[0] \!== asymptote && p2[0] \!== asymptote(3 ≠ 0) — asymptote guard passes- Flipped:
p1Flipped=[1,3],p2Flipped=[5,3] ratio = (3-0)/(3-0) = 1— opposite-sides guard passes (1 > 0)bExp = Math.log(1) / (1-5) = 0 / (-4) = 0bExp === 0guard fires → returnsundefined✓
The function returns correctly, but this path is never exercised by any test, and the JSDoc gives no hint this case is handled.
How to fix it:
- Add "same x" to the JSDoc list: "Returns undefined if the inputs are geometrically invalid (same y, same x, a point on the asymptote, or points on opposite sides of the asymptote)."
- Add a test:
expect(getLogarithmCoefficients([[3,1],[3,5]], 0)).toBeUndefined()— mirroring the existinggetExponentialCoefficientstest.
Impact:
Purely a documentation and test coverage gap. No functional impact on callers. However, downstream PRs 3–6 in this series will all rely on getLogarithmCoefficients, so establishing complete test coverage now is valuable before the function is widely consumed.
| return {a, b, c}; | ||
| } |
There was a problem hiding this comment.
🟣 This is a pre-existing bug in getExponentialCoefficients (not modified by this PR): when both input points share the same y-coordinate but neither lies on the asymptote, the function returns a degenerate {a, b:0, c} constant function instead of undefined. The fix is to add a b===0 guard to getExponentialCoefficients, mirroring the bExp===0 guard already present in the new getLogarithmCoefficients.
Extended reasoning...
What the bug is and how it manifests:
In getExponentialCoefficients, when both input points have the same y-value but neither equals the asymptote, the function silently returns {a, b:0, c} — a constant function — instead of undefined. A constant function is not a valid exponential, so this is a degenerate result that downstream callers may not anticipate.
The specific code path:
At lines 83–84 of coefficients.ts, when p1[1] === p2[1] and neither equals c, the ratio (p1[1] - c) / (p2[1] - c) equals 1. Then b = Math.log(1) / denom = 0. After that, a = (p1[1] - c) / Math.exp(0 * p1[0]) = p1[1] - c, which is non-zero. The only guards at lines 87–88 are \!isFinite(a) || \!isFinite(b) || a === 0. Since isFinite(0) is true and a \!== 0, all guards pass, and the function returns {a, b:0, c}.
Why existing guards don't prevent it:
The guard checks a === 0 but not b === 0. A zero b produces f(x) = a·e^{0·x} + c = a + c (a constant), which is geometrically meaningless as an exponential function. By contrast, the newly added getLogarithmCoefficients explicitly guards bExp === 0 for exactly this analogous scenario, as visible at line 136 of the PR diff.
Step-by-step proof:
getExponentialCoefficients([[0,5],[1,5]], 0):
p1[0]=0, p2[0]=1→ same-x guard passes (0 \!== 1)p1[1]=5, c=0→ asymptote guard passes (5 \!== 0)ratio = (5-0)/(5-0) = 1 > 0→ opposite-sides guard passesb = Math.log(1) / (0-1) = 0 / -1 = 0a = (5-0) / Math.exp(0*0) = 5 / 1 = 5- Guards:
\!isFinite(5)=false,\!isFinite(0)=false,a===0is false → all pass - Returns
{a:5, b:0, c:0}
This represents f(x) = 5·e^0 + 0 = 5, a constant, not an exponential.
Impact and fix:
This bug exists entirely in pre-existing code that this PR does not touch. However, since this PR explicitly models getLogarithmCoefficients after getExponentialCoefficients and the PR description draws direct comparisons, this is a natural opportunity to fix both. The fix is a single guard addition to getExponentialCoefficients: change a === 0 to a === 0 || b === 0 at line 88, which mirrors the bExp === 0 guard already present in getLogarithmCoefficients.
…3422) ## Summary: PR series to add logarithm graph support to the Interactive Graph widget: 1. [Add logarithm graph type definitions and data](#3420) 2. [Add logarithm math utilities to kmath](#3421) 3.▶️ [Add logarithm graph state management and reducer](#3422) 4. [Add logarithm graph rendering, SR strings, and equation string](#3423) 5. [Add logarithm graph scoring](#3424) 6. [Add logarithm graph option in the Interactive Graph Editor](#3425) Add logarithm graph state management and reducer for supporting Logarithm graph in Interactive Graph - Add `LogarithmGraphState` to the internal state type system - Wire up reducer actions (`movePoint` + `moveCenter`) with logarithm-specific constraints - Add graph state initialization with sensible defaults - Add test data fixtures and question builder support ## Details This PR adds the state management layer for logarithm graphs, following the exponential pattern throughout. Logarithm is the vertical-asymptote mirror of exponential's horizontal-asymptote design. **Action registration:** Reuses existing `movePoint` and `moveCenter` action creators (no new action types). The `actions` export object gets `logarithm: { movePoint, moveCenter }`, identical to exponential. **Reducer — `doMovePoint`:** - Point cannot land on the asymptote's x-coordinate - Both points must have different y-values (prevents degenerate coefficient computation) - Cross-asymptote reflection: when a point is dragged past the asymptote, the other point is reflected (`reflectedX = 2 * asymptoteX - otherX`) so both points end up on the same side — matches Grapher widget behavior **Reducer — `doMoveCenter`:** - Asymptote moves horizontally only (X component extracted, Y ignored) - Snap-through logic: when the new position would land between or on the curve points, snaps past all points using the midpoint heuristic for direction detection (prevents oscillation/flicker) - Final safety check: asymptote cannot land exactly on either point's x-coordinate **Initialization:** `getLogarithmCoords()` follows `getExponentialCoords()` pattern — returns `{coords, asymptote}`. Default coords use normalized fractions `[0.55, 0.55]` and `[0.75, 0.75]` to ensure both points are to the right of the default asymptote at x=0 after normalization (x=0.5 would land exactly on the asymptote). **Placeholders:** `mafs-graph.tsx` returns `{graph: null, interactiveElementsDescription: null}` for logarithm (replaced in PR 4). `mafs-state-to-interactive-graph.ts` has the real serialization (not a placeholder). Co-Authored by Claude Code (Opus) Issue: LEMS-3953 ## Test plan: - [ ] `pnpm tsc` passes - [ ] `pnpm knip` passes - [ ] `pnpm lint` passes - [ ] `pnpm prettier . --check` passes - [ ] Reducer tests pass (147 total, 14 new logarithm tests): - `movePoint`: same-y rejection, bounding-to-same-y rejection, valid move, on-asymptote rejection, cross-asymptote reflection - `moveCenter`: valid move, snap-through between points, Y-component ignored, final safety rejection - Initialization: given coords, startCoords, defaults - Gradable graph: logarithm state conversion - Serialization: logarithm state to interactive graph - [ ] Interactive graph tests pass (206 total, logarithm added to parameterized test maps) - [ ] Serialization tests pass (14 total) Author: ivyolamit Reviewers: claude[bot], ivyolamit, SonicScrewdriver Required Reviewers: Approved By: SonicScrewdriver Checks: ⏭️ 1 check has been skipped, ✅ 10 checks were successful, ⚪️ 1 check is neutral Pull Request URL: #3422
…uation string (#3423) ## Summary: PR series to add logarithm graph support to the Interactive Graph widget: 1. [Add logarithm graph type definitions and data](#3420) 2. [Add logarithm math utilities to kmath](#3421) 3. [Add logarithm graph state management and reducer](#3422) 4.▶️ [Add logarithm graph rendering, SR strings, and equation string](#3423) 5. [Add logarithm graph scoring](#3424) 6. [Add logarithm graph option in the Interactive Graph Editor](#3425) Create the logarithm graph visual component, add Storybook coverage, SR strings, and equation string for supporting Logarithm graph in Interactive Graph - Create the logarithm graph visual component (`logarithm.tsx`) with curve rendering, draggable asymptote, and movable points - Add 6 screen reader strings for accessibility - Add equation string display for the editor - Add Storybook story ## Details This is the largest PR in the series. It creates the visual rendering of the logarithm graph, following the exponential component pattern with the axis swapped (vertical asymptote instead of horizontal). **Curve rendering:** - Single `<Plot.OfX>` with domain restricted to one side of the asymptote (`[asymptoteX + 0.001, xMax]` or `[xMin, asymptoteX - 0.001]`) - Plot function computes `a * ln(b*x + c)`, returning NaN when outside domain or when y-value exceeds visible range + padding (prevents curve visually touching the asymptote) - `coeffRef` caches last valid coefficients as fallback during transient invalid states **Asymptote rendering:** - Uses existing `MovableAsymptote` component with `orientation="vertical"` - Dispatches `actions.logarithm.moveCenter()` on drag - `constrainAsymptoteKeyboard()` implements snap-through logic for keyboard navigation (mirrors exponential's vertical version but on X-axis) **Keyboard constraints:** - `getLogarithmKeyboardConstraint()` prevents points from landing on the asymptote's x-coordinate or sharing y-value with the other point - Uses bounded retry (max 3 steps) to skip past invalid positions **Equation string:** - `getLogarithmEquationString()` displays `y = a*ln(b*x + c)` with computed coefficient values - `defaultLogarithmCoords()` provides fallback coords (normalized fractions `[0.55, 0.55]`, `[0.75, 0.75]`) **Screen reader strings (6):** - `srLogarithmGraph` — graph container label - `srLogarithmPoint1` / `srLogarithmPoint2` — point position labels - `srLogarithmAsymptote` — asymptote label with keyboard instructions - `srLogarithmDescription` — graph state description - `srLogarithmInteractiveElements` — interactive elements summary Co-Authored by Claude Code (Opus) Issue: LEMS-3953 ## Test plan: - [ ] `pnpm tsc` passes - [ ] `pnpm knip` passes - [ ] `pnpm lint` passes - [ ] `pnpm prettier . --check` passes - [ ] Logarithm component tests pass (15 new tests): - 9 screen reader tests (aria-labels, descriptions, interactive elements, updates on state change) - 3 keyboard constraint tests for points (valid move, skip asymptote, skip same-y) - 3 keyboard constraint tests for asymptote (free move, snap-through, skip point x-value) - [ ] Interactive graph tests pass (178 passed, 28 skipped) - [ ] Verify rendering in Storybook (logarithm story renders correctly, curve matches expected shape) Author: ivyolamit Reviewers: claude[bot], ivyolamit, SonicScrewdriver Required Reviewers: Approved By: SonicScrewdriver Checks: ⏭️ 1 check has been skipped, ✅ 10 checks were successful Pull Request URL: #3423
## Summary: PR series to add logarithm graph support to the Interactive Graph widget: 1. [Add logarithm graph type definitions and data](#3420) 2. [Add logarithm math utilities to kmath](#3421) 3. [Add logarithm graph state management and reducer](#3422) 4. [Add logarithm graph rendering, SR strings, and equation string](#3423) 5.▶️ [Add logarithm graph scoring](#3424) 6. [Add logarithm graph option in the Interactive Graph Editor](#3425) Add logarithm graph scoring to support the Logarithm graph in Interactive Graph - Add logarithm scoring to `score-interactive-graph.ts` using direct coefficient comparison - No canonical normalization needed (same as exponential) ## Details Follows the exponential scoring pattern exactly. Computes `{a, b, c}` coefficients for both the user's answer and the rubric using `getLogarithmCoefficients` from kmath, then compares with `approximateDeepEqual`. **Scoring logic:** - Returns `invalid` if guess coords or asymptote are missing, or if coefficient computation fails for either side - Returns `earned: 1` if `[a, b, c]` coefficients approximately match - Returns `earned: 0` otherwise This means two different sets of control points that define the same logarithmic curve will score as correct — the comparison is on the mathematical function, not the specific points chosen. Co-Authored by Claude Code (Opus) Issue: LEMS-3953 ## Test plan: - [ ] `pnpm tsc` passes - [ ] `pnpm knip` passes - [ ] `pnpm lint` passes - [ ] `pnpm prettier . --check` passes - [ ] Scoring tests pass (44 total, 6 new logarithm tests): - [ ] Invalid: undefined guess, null coords, null asymptote - [ ] Correct: matching coefficients - [ ] Incorrect: different coefficients - [ ] Equivalent curves: different control points producing same `y = ln(x)` coefficients → correct Author: ivyolamit Reviewers: claude[bot], SonicScrewdriver Required Reviewers: Approved By: SonicScrewdriver Checks: ⏭️ 1 check has been skipped, ✅ 10 checks were successful Pull Request URL: #3424
…ph Editor (#3425) ## Summary: PR series to add logarithm graph support to the Interactive Graph widget: 1. [Add logarithm graph type definitions and data](#3420) 2. [Add logarithm math utilities to kmath](#3421) 3. [Add logarithm graph state management and reducer](#3422) 4. [Add logarithm graph rendering, SR strings, and equation string](#3423) 5. [Add logarithm graph scoring](#3424) 6.▶️ [Add logarithm graph option in the Interactive Graph Editor](#3425) Add logarithm graph option in the Interactive Graph Editor - Add `StartCoordsLogarithm` component for configuring logarithm start coordinates in the editor - Gate the "Logarithm function" option behind the `interactive-graph-logarithm` feature flag - Export `getLogarithmCoords` for use by the editor - Add start asymptote validation ## Details This PR enables content creators to configure logarithm exercises in the Interactive Graph editor, following the exponential editor pattern. **StartCoordsLogarithm component:** - Two coordinate pair inputs (Point 1 and Point 2) - A single number input for the asymptote x-position (labeled "Asymptote x =") - Equation display showing `y = a * ln(b*x + c)` with computed coefficient values - CSS module styling (not Aphrodite), following `start-coords-exponential.module.css` **Feature flag:** "Logarithm function" appears in the graph type selector only when `interactive-graph-logarithm` is enabled, preventing content creators from selecting it until it's ready for launch. **Editor validation:** The start asymptote x-value is validated — it cannot fall between or on the x-coordinates of the curve's start points (mirrors exponential's y-axis validation but for x-axis). **Exports:** `getLogarithmCoords()` is now exported from `initialize-graph-state.ts` and re-exported from `@khanacademy/perseus` for use by the editor's start-coords UI. Co-Authored by Claude Code (Opus) Issue: LEMS-3969 ## Test plan: - [ ] `pnpm tsc` passes - [ ] `pnpm knip` passes - [ ] `pnpm lint` passes - [ ] `pnpm prettier . --check` passes - [ ] Editor tests pass (422 total across 15 suites) - [ ] Verify in Storybook: "Logarithm function" appears in graph type selector when feature flag is on - [ ] Verify start coords UI renders correctly with asymptote input and equation display Author: ivyolamit Reviewers: claude[bot], SonicScrewdriver, ivyolamit Required Reviewers: Approved By: SonicScrewdriver Checks: ⏭️ 1 check has been skipped, ✅ 10 checks were successful Pull Request URL: #3425
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @khanacademy/[email protected] ### Minor Changes - [#3421](#3421) [`9099a40a4e`](9099a40) Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add the logarithm math utilities to kmath for supporting Logarithm graph in Interactive Graph ### Patch Changes - Updated dependencies \[[`a72f7db4a3`](a72f7db)]: - @khanacademy/[email protected] ## @khanacademy/[email protected] ### Minor Changes - [#3422](#3422) [`a4eaa5a1f8`](a4eaa5a) Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add logarithm graph state management and reducer for supporting Logarithm graph in Interactive Graph - [#3425](#3425) [`2c36b8a4c3`](2c36b8a) Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add logarithm graph option in the Interactive Graph Editor - [#3423](#3423) [`3cc56f60dd`](3cc56f6) Thanks [@ivyolamit](https://github.com/ivyolamit)! - Create the logarithm graph visual component, add Storybook coverage, SR strings, and equation string for supporting Logarithm graph in Interactive Graph - [#3420](#3420) [`a72f7db4a3`](a72f7db) Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add logarithm type definitions, this is the initial implementation for supporting Logarithm graph in Interactive Graph widget. ### Patch Changes - [#3461](#3461) [`0481b4238d`](0481b42) Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (DX) | Update CSS modules to kabab case - [#3455](#3455) [`0d6cf3c3ce`](0d6cf3c) Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (UX) | Fix messed up Graphie labels behind scale feature flag - Updated dependencies \[[`08b1218b62`](08b1218), [`a72f7db4a3`](a72f7db), [`9099a40a4e`](9099a40)]: - @khanacademy/[email protected] - @khanacademy/[email protected] - @khanacademy/[email protected] - @khanacademy/[email protected] - @khanacademy/[email protected] - @khanacademy/[email protected] ## @khanacademy/[email protected] ### Minor Changes - [#3420](#3420) [`a72f7db4a3`](a72f7db) Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add logarithm type definitions, this is the initial implementation for supporting Logarithm graph in Interactive Graph widget. ## @khanacademy/[email protected] ### Minor Changes - [#3425](#3425) [`2c36b8a4c3`](2c36b8a) Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add logarithm graph option in the Interactive Graph Editor - [#3420](#3420) [`a72f7db4a3`](a72f7db) Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add logarithm type definitions, this is the initial implementation for supporting Logarithm graph in Interactive Graph widget. ### Patch Changes - [#3461](#3461) [`0481b4238d`](0481b42) Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (DX) | Update CSS modules to kabab case - Updated dependencies \[[`a4eaa5a1f8`](a4eaa5a), [`2c36b8a4c3`](2c36b8a), [`3cc56f60dd`](3cc56f6), [`08b1218b62`](08b1218), [`a72f7db4a3`](a72f7db), [`9099a40a4e`](9099a40), [`0481b4238d`](0481b42), [`0d6cf3c3ce`](0d6cf3c)]: - @khanacademy/[email protected] - @khanacademy/[email protected] - @khanacademy/[email protected] - @khanacademy/[email protected] - @khanacademy/[email protected] - @khanacademy/[email protected] - @khanacademy/[email protected] ## @khanacademy/[email protected] ### Minor Changes - [#3424](#3424) [`08b1218b62`](08b1218) Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add logarithm graph scoring to support the Logarithm graph in Interactive Graph ### Patch Changes - Updated dependencies \[[`a72f7db4a3`](a72f7db), [`9099a40a4e`](9099a40)]: - @khanacademy/[email protected] - @khanacademy/[email protected] ## @khanacademy/[email protected] ### Patch Changes - Updated dependencies \[[`a72f7db4a3`](a72f7db)]: - @khanacademy/[email protected] ## @khanacademy/[email protected] ### Patch Changes - Updated dependencies \[[`a72f7db4a3`](a72f7db)]: - @khanacademy/[email protected] - @khanacademy/[email protected] ## @khanacademy/[email protected] ### Patch Changes - Updated dependencies \[[`a72f7db4a3`](a72f7db), [`9099a40a4e`](9099a40)]: - @khanacademy/[email protected] - @khanacademy/[email protected]
Summary:
PR series to add logarithm graph support to the Interactive Graph widget:
Add the logarithm math utilities to kmath for supporting Logarithm graph in Interactive Graph
LogarithmCoefficienttype andgetLogarithmCoefficients()to kmath, following the exponential pattern{a, b, c}forf(x) = a·ln(b·x + c)using the inverse exponential approachDetails
This adds the shared math utility for logarithm coefficient computation to
@khanacademy/kmath, following the same pattern asgetExponentialCoefficients(). Both the rendering component (PR 4) and scoring (PR 5) will consume this function.Mathematical approach (inverse exponential):
(x, y) → (y, x)— treating the logarithm as the inverse of an exponentialccoefficientaExp,bExpfrom the flipped pointsa = 1/bExp,b = 1/aExp,c = -cExp/aExpValidation guards (returns
undefinedfor invalid inputs):bExpundefined)This matches the reference implementation in
packages/perseus-core/src/utils/grapher-util.ts(the Grapher widget'sLogarithmobject, lines 449–558).Co-Authored by Claude Code (Opus)
Issue: LEMS-3953
Test plan
pnpm tscpassespnpm knippassespnpm lintpassespnpm prettier . --checkpasses[-4,-3],[-5,-7], asymptote-6reproduces correct y-values[1,0],[e,1], asymptote0→a≈1, b≈1, c≈0y = ln(-x)) →b≈-1undefinedundefinedundefined